package zasm.zasm.listing;

import static zasm.zasm.listing.TextListingWriter.MAX_COLUMN_WIDTH;
import static zasm.zasm.util.ValidationUtils.illegalArg;
import static zasm.zasm.util.ValidationUtils.validateArgBetween;
import static zasm.zasm.util.ValidationUtils.validateArgDivides;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Writer;

public class TextListingWriterBuilder {

	public static final boolean	DEFAULT_USE_SPACES						= false;
	public static final int		DEFAULT_TAB_WIDTH						= 8;
	public static final int		DEFAULT_LINE_NUMBER_WIDTH				= 99999;
	public static final int		DEFAULT_FILE_NEST_WIDTH					= 2;
	public static final int		DEFAULT_LINE_NUMBER_AND_FILE_NEST_WIDTH	= 8;
	public static final int		DEFAULT_ORIGIN_SIZE						= 16;
	public static final int		DEFAULT_ORIGIN_WIDTH					= 8;
	public static final int		DEFAULT_OUT_WIDTH						= 16;

	private static final String	TAB_WIDTH_NAME							= "tab width";
	private static final String	LINE_NUMBER_WIDTH_NAME					= "line number width";
	private static final String	FILE_NEST_WIDTH_NAME					= "file nest width";
	private static final String	LINE_NUMBER_AND_FILE_NEST_WIDTH_NAME	= "line number and file nest width";
	private static final String	ORIGIN_INT_SIZE_NAME					= "origin int size";
	private static final String	ORIGIN_WIDTH_NAME						= "origin width";
	private static final String	OUT_WIDTH_NAME							= "output width";

	private boolean				useSpaces								= DEFAULT_USE_SPACES;
	private int					tabWidth								= DEFAULT_TAB_WIDTH;
	private int					lineNumberWidth							= DEFAULT_LINE_NUMBER_WIDTH;
	private int					fileNestWidth							= DEFAULT_FILE_NEST_WIDTH;
	private int					originIntSize							= DEFAULT_ORIGIN_SIZE;
	private int					originWidth								= DEFAULT_ORIGIN_WIDTH;
	private int					outWidth								= DEFAULT_OUT_WIDTH;

	public TextListingWriterBuilder() {}

	public boolean usesSpaces() {
		return useSpaces;
	}

	@SuppressWarnings("hiding")
	public TextListingWriterBuilder useSpaces(final boolean useSpaces) {
		if (useSpaces) {
			return useSpaces();
		} else {
			return useTabs();
		}
	}

	public TextListingWriterBuilder useSpaces() {
		useSpaces = true;
		return this;
	}

	public TextListingWriterBuilder useTabs() {
		useSpaces = false;
		setTabWidth(tabWidth);
		return this;
	}

	public int getTabWidth() {
		return tabWidth;
	}

	public TextListingWriterBuilder setTabWidth(final int tabWidth) {
		validateArgBetween(tabWidth, 1, MAX_COLUMN_WIDTH, TAB_WIDTH_NAME);
		this.tabWidth = tabWidth;
		return this;
	}

	public int getLineNumberWidth() {
		return lineNumberWidth;
	}

	public void setLineNumberWidth(final int lineNumberWidth) {
		validateArgBetween(lineNumberWidth, 0, MAX_COLUMN_WIDTH - 1, LINE_NUMBER_WIDTH_NAME);
		this.lineNumberWidth = lineNumberWidth;
	}

	public int getFileNestWidth() {
		return fileNestWidth;
	}

	public void setFileNestWidth(final int fileNestWidth) {
		validateArgBetween(fileNestWidth, 0, MAX_COLUMN_WIDTH - 1, FILE_NEST_WIDTH_NAME);
		this.fileNestWidth = fileNestWidth;
	}

	public int getOriginIntSize() {
		return originIntSize;
	}

	public void setOriginIntSize(final int originIntSize) {
		validateArgBetween(originIntSize, 0, Integer.SIZE, ORIGIN_INT_SIZE_NAME);
		this.originIntSize = originIntSize;
	}

	protected int getOriginIntWidth() {
		return (originIntSize + 3) / 4;
	}

	public int getOriginWidth() {
		return originWidth;
	}

	public TextListingWriterBuilder setOriginWidth(final int originWidth) {
		validateArgBetween(originWidth, 0, MAX_COLUMN_WIDTH, ORIGIN_WIDTH_NAME);
		this.originWidth = originWidth;
		return this;
	}

	public int getOutWidth() {
		return outWidth;
	}

	public TextListingWriterBuilder setOutWidth(final int outWidth) {
		validateArgBetween(outWidth, 3, MAX_COLUMN_WIDTH, OUT_WIDTH_NAME);
		this.outWidth = outWidth;
		return this;
	}

	protected void validate() {
		if (!useSpaces) {
			validateArgDivides(tabWidth, lineNumberWidth + fileNestWidth, TAB_WIDTH_NAME,
					LINE_NUMBER_AND_FILE_NEST_WIDTH_NAME);
			validateArgDivides(tabWidth, originWidth, TAB_WIDTH_NAME, ORIGIN_WIDTH_NAME);
			validateArgDivides(tabWidth, outWidth, TAB_WIDTH_NAME, OUT_WIDTH_NAME);
		}
		if ((originIntSize != 0) && (getOriginIntWidth() >= originWidth)) {
			illegalArg(originIntSize, originWidth, ORIGIN_INT_SIZE_NAME, "fit with at least one space in",
					ORIGIN_WIDTH_NAME);
		}
	}

	public ListingWriter toWriter(final Writer writer) {
		validate();
		return new DefaultTextListingWriter(writer, tabWidth, useSpaces, lineNumberWidth, fileNestWidth,
				getOriginIntWidth(), originWidth, outWidth);
	}

	public ListingWriter toWriter(final File file) throws FileNotFoundException {
		validate();
		return new DefaultTextListingWriter(file, tabWidth, useSpaces, lineNumberWidth, fileNestWidth,
				getOriginIntWidth(), originWidth, outWidth);
	}

	public ListingWriter toWriter(final String fileName) throws FileNotFoundException {
		validate();
		return new DefaultTextListingWriter(fileName, tabWidth, useSpaces, lineNumberWidth, fileNestWidth,
				getOriginIntWidth(), originWidth, outWidth);
	}

}
